﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Flagwind.Collections;
using Flagwind.Data.Configuration;

namespace Flagwind.Data
{
	/// <summary>
	/// 表示数据访问的基础类。
	/// </summary>
	public class DataAccess : IDataAccess
	{
		#region 私有字段

		private IDataProvider _defaultProvider;
		private DataAccessSettings _settings;
		private NamedCollection<IDataProvider> _dataProviders;

		#endregion

		#region 保护属性

		protected IDataProvider ExecuteProvider
		{
			get
			{
				if(_defaultProvider == null)
					_defaultProvider =  DataProviderFactory.GetProvider();

				return _defaultProvider;
			}
		}

		#endregion

		#region 公共属性

		public IDataProvider DefaultProvider
		{
			get
			{
				if(_defaultProvider == null)
					return DataProviderFactory.DefaultProvider;

				return _defaultProvider;
			}
			set
			{
				_defaultProvider = value;
			}
		}

		public NamedCollection<IDataProvider> DataProviders
		{
			get
			{
				return _dataProviders;
			}
		}

		public DataAccessSettings Settings
		{
			get
			{
				return _settings;
			}
			set
			{
				if(value == null)
					throw new ArgumentNullException();

				_settings = value;
			}
		}

		#endregion

		#region 构造方法

		/// <summary>
		/// 初始化 DataAccess 类的新实例。
		/// </summary>
		protected internal DataAccess()
		{
		}

		/// <summary>
		/// 初始化 DataAccess 类的新实例。
		/// </summary>
		/// <param name="dataProvider">数据访问提供程序。</param>
		public DataAccess(IDataProvider dataProvider)
		{
			this.DefaultProvider = dataProvider;
		}

		public DataAccess(GeneralOption config)
		{
			this._settings = new DataAccessSettings();
			this._settings.EnableLog = config.EnableLog;
			this._settings.ExecuteTime = config.ExecuteTime;

			var providers = new List<IDataProvider>();

			foreach(var item in config.Providers)
			{
				var element = (ProviderElement)item;
				var provider = DataProviderFactory.GetProvider(element.Name, element.Type, element.ConnectionString);

				providers.Add(provider);
			}

			this._settings.DataProviders = this._dataProviders = new NamedCollection<IDataProvider>(providers, provider => provider.Name);

			DataAccessSettings.Default = this._settings;

			this._defaultProvider = DataProviderFactory.GetProvider();
		}

		#endregion

		#region IDataAccess 成员

		#region 新增方法

		public int Insert(IEntity entity)
		{
			return this.Insert(entity, this.DefaultProvider);
		}

		public int Insert(IEntity entity, IDataProvider provider)
		{
			int fieldCount = entity.GetPropertyNames().Length;
			if(fieldCount == 0)
			{
				throw new ArgumentException("实体类属性字段为空。");
			}
			return DataUtility.Insert(entity, entity.GetPropertyChangedList(), provider);
		}

		public int Insert(IEnumerable<IEntity> entities)
		{
			return this.Insert(entities, this.ExecuteProvider);
		}

		public int Insert(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			int count = 0;
			if(entities.Any())
			{
				provider.BeginTransaction();
				try
				{
					count += entities.Sum(entity => DataUtility.Insert(entity, entity.GetPropertyChangedList(), provider));
					provider.Commit();
				}
				catch(Exception ex)
				{
					provider.Rollback();
					throw ex;
				}
			}
			return count;
		}

		#endregion

		#region 更新方法

		public int Update(IEntity entity)
		{
			return this.Update(entity, this.DefaultProvider);
		}

		public int Update(IEntity entity, IDataProvider provider)
		{
			return DataUtility.Update(entity, entity.GetPropertyChangedList(), provider);
		}

		public int Update(IEnumerable<IEntity> entities)
		{
			return this.Update(entities, this.ExecuteProvider);
		}

		public int Update(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			int count = 0;
			provider.BeginTransaction();
			try
			{
				count += entities.Sum(entity => DataUtility.Update(entity, entity.GetPropertyChangedList(), provider));
				provider.Commit();
			}
			catch(Exception ex)
			{
				provider.Rollback();
				throw ex;
			}
			return count;
		}

		#endregion

		#region 删除方法

		public int Delete(IEntity entity)
		{
			return DataUtility.Delete(entity, this.DefaultProvider);
		}

		public int Delete(IEntity entity, IDataProvider provider)
		{
			return DataUtility.Delete(entity, provider);
		}

		public int Delete(IEnumerable<IEntity> entities)
		{
			return this.Delete(entities, this.ExecuteProvider);
		}

		public int Delete(IEnumerable<IEntity> entities, IDataProvider provider)
		{
			int count = 0;
			provider.BeginTransaction();
			try
			{
				count += entities.Sum(entity => DataUtility.Delete(entity, provider));
				provider.Commit();
			}
			catch(Exception ex)
			{
				provider.Rollback();
				throw ex;
			}
			return count;
		}

		#endregion

		#region 查询方法

		public IEnumerable<T> Select<T>(Command command) where T : class, IEntity, new()
		{
			return this.Select<T>(command, this.DefaultProvider);
		}

		public IEnumerable<T> Select<T>(Command command, IDataProvider provider) where T : class, IEntity, new()
		{
			// 如果开启了分页且记录总数为0，直接返回空集合
			if(command.PageEnable)
			{
				if(command.RecordCount == 0)
				{
					if(!command.haveOrderBy)
					{
						throw new Exception("如果要进行分页并且同时获得记录总数，Command必须有排序操作，请调用OrderBy方法。");
					}
					T entity = this.Find<T>(command, provider);
					command.RecordCount = Utility.ConvertType<int>(entity.GetPropertyValues()[0]);
					//如果记录总数仍然是0，直接返回空集合。
					if(command.RecordCount == 0)
					{
						return new List<T>();
					}
				}
			}
			var reader = AnonymousAccess.GetReader(command, typeof(T), provider);
			return DataUtility.ResolveEntityList<T>(reader, command.GetEntityTableName());
		}

		public T Find<T>(Command command) where T : class
		{
			return this.Find<T>(command, this.DefaultProvider);
		}

		public T Find<T>(Command command, IDataProvider provider) where T : class
		{
			var reader = AnonymousAccess.GetReader(command, typeof(T), provider, true);
			return DataUtility.ResolveEntity<T>(reader);
		}

		public bool Fill(IEntity entity)
		{
			return this.Fill(entity, this.DefaultProvider);
		}

		public bool Fill(IEntity entity, IDataProvider provider)
		{
			return DataUtility.Fill(entity, provider);
		}

		/// <summary>
		/// 检测实体类是否在数据库中存在，注：必须包含主键值。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		public bool Exists(IEntity entity)
		{
			return this.Exists(entity, this.DefaultProvider);
		}

		/// <summary>
		/// 根据指定的数据访问提供程序，检测实体类是否在数据库中存在，注：必须包含主键值。
		/// </summary>
		/// <param name="entity">包含主键值的实体对象。</param>
		/// <param name="provider">数据访问提供程序。</param>
		/// <returns>布尔值，表示是否存在。</returns>
		public bool Exists(IEntity entity, IDataProvider provider)
		{
			return DataUtility.Exists(entity, provider);
		}

		public bool Exists(Command command)
		{
			return this.Exists(command, this.DefaultProvider);
		}

		public bool Exists(Command command, IDataProvider provider)
		{
			var count = AnonymousAccess.Count(command, provider);
			return Utility.ConvertType<int>(count) > 0;
		}

		#endregion

		#region 执行方法

		public int Execute(Command command)
		{
			return this.Execute(command, this.DefaultProvider);
		}

		public int Execute(Command command, IDataProvider provider)
		{
			string sql = command.ToString();
			command.Dispose();
			if(command.Parameters.Count > 0)
			{
				var parameters = DataUtility.GetParameters(command.Parameters, provider);
				return provider.ExecuteNonQuery(sql, CommandType.Text, parameters);
			}
			else
			{
				return provider.ExecuteNonQuery(sql);
			}
		}

		#endregion

		#region 事务处理

		/// <summary>
		/// 打开连接并开启事务。
		/// </summary>
		public void BeginTransaction()
		{
			_defaultProvider.BeginTransaction();
		}

		/// <summary>
		/// 开启事务并指定事务隔离级别。
		/// </summary>
		/// <param name="level">事务隔离级别。</param>
		public void BeginTransaction(IsolationLevel level)
		{
			_defaultProvider.BeginTransaction(level);
		}

		/// <summary>
		/// 提交事务并关闭连接。
		/// </summary>
		public void Commit()
		{
			_defaultProvider.Commit();
		}

		/// <summary>
		/// 回滚事务并关闭连接。
		/// </summary>
		public void Rollback()
		{
			_defaultProvider.Rollback();
		}

		#endregion

		#endregion
	}
}